//  IDVI 1.0 source copyright 1996 Garth A. Dickie
//
//  This source is free for non-commercial use.  No warranty, etc.
//  Please acknowledge reuse by including the line:
//
//  "Based in part on IDVI 1.0 source copyright 1996 Garth A. Dickie"
//
//  in your documentation and source code.  For commercial use or
//  distribution, please contact the author.  Please also send
//  questions, comments, bug reports, or fixes.
//
//  Best Regards,
//  Garth A. Dickie
//  dickie@elastic.avid.com

package ibook.v10.awt;

import java.awt.*;
import java.util.*;

public class RegularLayout implements LayoutManager {
    final private static int kComponentTableInitialSize = 13;

    private Hashtable   componentTable = new Hashtable( kComponentTableInitialSize );
    private Rectangle   bounds = new Rectangle( );
    private int         stretchX;
    private boolean     stretchXSet = false;
    private int         stretchY;
    private boolean     stretchYSet = false;
    private Dimension   gap;

    public RegularLayout( int gapX, int gapY ) {
        gap = new Dimension( gapX, gapY );
    }

    public RegularLayout( ) {
        this( 0, 0 );
    }

    public synchronized Component add( Component component, int x, int y, int width, int height ) {
        Rectangle componentBounds = new Rectangle( x, y, width, height );

        componentTable.put( component, componentBounds );

        if( bounds.isEmpty( ))
            bounds.reshape( x, y, width, height );
        else
            bounds.add( componentBounds );
        
        return component;
    }

    public Component add( Component component, int x, int y ) {
        return add( component, x, y, 1, 1 );
    }

    public synchronized void setStretchColumn( int x ) {
        stretchX = x;
        stretchXSet = true;
    }

    public synchronized void setStretchRow( int y ) {
        stretchY = y;
        stretchYSet = true;
    }

    public synchronized void setBounds( int x, int y, int width, int height ) {
        bounds.reshape( x, y, width, height );
    }

    public void addLayoutComponent( String name, Component component ) {
    }

    public void removeLayoutComponent( Component component ) {
    }

    public synchronized Dimension preferredLayoutSize( Container parent ) {
        Dimension cellSize = preferredCellSize( );
        Insets insets = parent.insets( );

        int unitWidth  = cellSize.width  + gap.width;
        int unitHeight = cellSize.height + gap.height;

        return new Dimension(
            insets.left + insets.right  + bounds.width  * unitWidth  + gap.width,
            insets.top  + insets.bottom + bounds.height * unitHeight + gap.height );
    }

    public synchronized Dimension minimumLayoutSize( Container parent ) {
        Dimension cellSize = minimumCellSize( );
        Insets insets = parent.insets( );

        int unitWidth  = cellSize.width  + gap.width;
        int unitHeight = cellSize.height + gap.height;

        return new Dimension(
            insets.left + insets.right  + bounds.width  * unitWidth  + gap.width,
            insets.top  + insets.bottom + bounds.height * unitHeight + gap.height );
    }

    public synchronized void layoutContainer( Container parent ) {
        Dimension cellSize = preferredCellSize( );
        Insets insets = parent.insets( );
        Dimension size = parent.size( );

        int unitWidth  = cellSize.width  + gap.width;
        int unitHeight = cellSize.height + gap.height;

        int usedWidth  = insets.left + insets.right  + bounds.width  * unitWidth  + gap.width;
        int usedHeight = insets.top  + insets.bottom + bounds.height * unitHeight + gap.height;

        int extraWidth  = size.width  - usedWidth;
        int extraHeight = size.height - usedHeight;

        int stretchWidth  = extraWidth  > 0 && stretchXSet ? extraWidth  : 0;
        int stretchHeight = extraHeight > 0 && stretchYSet ? extraHeight : 0;

        unitWidth  += extraWidth  > 0 && ! stretchXSet ? extraWidth  / bounds.width  : 0;
        unitHeight += extraHeight > 0 && ! stretchYSet ? extraHeight / bounds.height : 0;

        Enumeration keys = componentTable.keys( );
        while( keys.hasMoreElements( )) {
            Component component = ( Component ) keys.nextElement( );
            Rectangle componentBounds = ( Rectangle ) componentTable.get( component );

            int cellLeft   = componentBounds.x - bounds.x;
            int cellTop    = componentBounds.y - bounds.y;
            int cellRight  = cellLeft + componentBounds.width;
            int cellBottom = cellTop  + componentBounds.height;

            int left   = insets.left + gap.width  + cellLeft   * unitWidth;
            int top    = insets.top  + gap.height + cellTop    * unitHeight;
            int right  = insets.left +              cellRight  * unitWidth;
            int bottom = insets.top  +              cellBottom * unitHeight;

            left   += cellLeft   > stretchX ? stretchWidth  : 0;
            top    += cellTop    > stretchY ? stretchHeight : 0;
            right  += cellRight  > stretchX ? stretchWidth  : 0;
            bottom += cellBottom > stretchY ? stretchHeight : 0;

            component.reshape( left, top, right - left, bottom - top );
        }
    }

    private Dimension preferredCellSize( ) {
        int         cellWidth = 0;
        int         cellHeight = 0;

        Enumeration keys = componentTable.keys( );
        while( keys.hasMoreElements( )) {
            Component component = ( Component ) keys.nextElement( );
            Rectangle componentBounds = ( Rectangle ) componentTable.get( component );
            Dimension componentSize = component.preferredSize( );

            int componentCellWidth = 
                ( componentSize.width + componentBounds.width - 1 ) / componentBounds.width;
            int componentCellHeight =
                ( componentSize.height + componentBounds.height - 1 ) / componentBounds.height;

            if( componentCellWidth > cellWidth )
                cellWidth = componentCellWidth;
            if( componentCellHeight > cellHeight )
                cellHeight = componentCellHeight;
        }

        return new Dimension( cellWidth, cellHeight );
    }

    private Dimension minimumCellSize( ) {
        return preferredCellSize( );
    }
}
